Simple and Intuitive Server Push with a Chat Room Example

From Documentation
DocumentationSmall Talks2007AugustSimple and Intuitive Server Push with a Chat Room Example
Simple and Intuitive Server Push with a Chat Room Example

Author
Robbie Cheng, Engineer, Potix Corporation
Date
August 21, 2007
Version
Applicable to ZK 2.5 Freshly (zk-2.5.0-FL-2007-08-21 and later)

Stop.png This documentation is for an older version of ZK. For the latest one, please click here.


Introduction

Server push is a term which means to put content from the server to the client actively, but the browser won’t allow this behavior to happen. However, there is an old-saying, “Life finds a way.” Thus, there are many ways to emulate server push. Three of the most famous examples are comet, client-polling, and piggyback.


What can server push do for you?

Server push could help you to send content to user if necessary. Email and Chatroom are two famous examples. The email server will notify users if there is new arrival email in his/her email, and Chatroom will broadcast new messages to users if there is any new incoming message.

Now, ZK supports server push, and you could implement your own way to do server push by implementing org.zkoss.zk.ui.sys.ServerPush Interface. Don’t scowl too early since ZK has already constructed an implementation (org.zkoss.zk.ui.impl.PollingServerPush) for you using client-polling technique. The client will query the server repetitively, and the frequency will be adjusted automatically depending on the loading of the server. Then, the sever will send content back to the client if necessary.

In the following paragraph, I am going to demonstrate you how to use server push to implement a real application.


Live demo of Chat Room

Today, we are going to implement a chat room. Before we go through the detail of implementation, let’s experience it fist.

All right, can’t wait to see how to implement this, let’s check it out!

Build a real application

In this example of chat room, we have to update each user's content if there is any incoming new message. Then, who is going to be responsible for this job? Apparently, we need another thread to make sure whether there is new message or not, and then sends new incoming messages to online users by updating the content of his/her desktop (aka a browser). And we need a chatroom to maintaining the chatting list, and broadcasts messages to each thread. The content of each desktop will be maintained by one thread(a Chatter). The architecture of the chatroom is as follows,

Chatroom.jpg


The Chat Room Class

The chat room which is responsible for maintaining the chatting list, and broadcasts messages to each thread (aka. a Chatter) is as follows,

    public class ChatRoom {
     private Collection _chatters;
     private static final String SIGNAL = "~~~";

     public ChatRoom() {
      _chatters = new LinkedList();
     }
     public void broadcast(String sender, String message) {}
     public void subscribe(Chatter chatter) {}
     public void unsubscribe(Chatter chatter) {}
     //the implementation is omitted, please refer to the source code
  • subscribe(), subscribes when a new user join the chat room
  • unsubscribe(), unsubscribes when a user leaves the chat room
  • broadcast(), broadcasts new messages to each Thread


How to Access Desktop in the Chatter Thread

In ZK, to avoid the problem of synchronization, a desktop could be accessed by one thread per time. To guarantee that a desktop won’t be accessed by multiple threads at the same time, we have to invoke the following two methods before and after accessing the desktop independently.

  • Activate the Desktop before using it
Invoke Executions.activate(Desktop) to get full control of the desktop.
  • Deactivate Desktop after using it
Invoke Executioins.deactivate(Desktop) to release full control of the desktop, after the thread finished its job

Part of the souce code of Chatter Thread are listed below,

    public class Chatter extends Thread {
     private List _msgs;
     ...................
     
     public Chatter(ChatRoom chatroom, String name, Component msgBoard) {
     ...................
     }
     public void run() {
      _chatroom.subscribe(this);
      try {
       while (!_ceased) {
        try {
         if (_msgs.isEmpty()) {
          Threads.sleep(500);// Update each 0.5 seconds
         } else {
          Executions.activate(_desktop); // activate the desktop
          try {
           process();
          } finally {
          Executions.deactivate(_desktop); //deactivate the desktop
          }
         }
        } catch (DesktopUnavailableException ex) {
         throw ex;
        } catch (Throwable ex) {
         log.error(ex);
         throw UiException.Aide.wrap(ex);
        }
       }
      } finally {
       _chatroom.unsubscribe(this);
      }
     }
     
      private void process() throws Exception {
      renderMessages();
     }
     
     private void renderMessages() {
      while (!_msgs.isEmpty()) {
       String msg;
       synchronized (_msgs) {
        msg = _msgs.remove(0);
       }
        _msgBoard.appendChild(new Label(msg)); //update the UI component if there is new message
      }
     }
     ....................
     }
  • renderMessages(), add new messages to desktop dominated by the thread


User Interface Pages

The UI pages is simple, pleas take a look at the following listing.

<window id="win" title="ZK Chat Room" width="300px" border="normal" use="org.zkforge.chat.ChatWindow">
 <div id="dv" visible="false" style="height: 300px; overflow:scroll">
	<vbox id="msgBoard" />
 </div>
 <vbox id="input" visible="false">
  <textbox id="msg" style="width:250px"/>
  <hbox>
   <button id="submit" label="send" forward="onSendMsg"/>
   <button id="exit" label="exit" forward="onExit"/>
  </hbox>
 </vbox>
 <div id="login" style="width: 200px">
	<vbox>
	 <label value="Enter Chat" style="font-weight: bold"/>
	 <hbox>
		NickName:<textbox id="nickname" constraint="no empty"/>
	 </hbox>
	 <button label="submit" forward="onLogin"/>     
	</vbox>
 </div>
</window>
  • forward attribute is used to forward events to other components when its certain event is invoked


How to Enable Server Push for the Desktop

Here comes another question-who is responsible for invoking this thread to do its job? Don’t worry, ZK will handle this for you, and the only thing you have to do is to enable Server Push with the following instructions.

In fact, there are only two steps to enable Server Push as follows,


Enable Server Push for the Desktop

  • Use Desktop.enableServerPush(boolean bool) to enable server push for the Desktop


Register the thread in the Desktop

Simply create a new thread in the desktop, and this thread will be invoked if Server Push has been enabled for the desktop.

Next, let's take a look at a code snippet from the controller of chat room when a user joins or leaves the chat room as follow,

public class ChatWindow extends Window {
 ......................
 public void onLogin() {  
  desktop.enableServerPush(true); // enable server push for this desktop
	
  sender = ((Textbox) getFellow("nickname")).getValue();  
  
  chatter = new Chatter(chatroom, sender, getFellow("msgBoard"));
	chatter.start(); // start the chatter thread

  // chage state of user
  setLogin(true);

  // refresh UI
  ((Textbox) getFellow("nickname")).setRawValue("");
  getFellow("login").setVisible(false);
  getFellow("dv").setVisible(true);
  getFellow("input").setVisible(true);
 }

 public void onExit() {
  chatter.setDone(); // stop the chatter thread    
  desktop.enableServerPush(false); //disable server push  
  setLogin(false);
  
  // refresh the UI
  getFellow("msgBoard").getChildren().clear();
  getFellow("login").setVisible(true);
  getFellow("dv").setVisible(false);
  getFellow("input").setVisible(false);
 }
 ....................
}


How to adjust the time interval of Polling

As I mentioned in previous paragraph, PollingServerPush is a client-driven Server Push mechanism, as a result, you could adjust the time interval of polling by invoking Executions.setDelay(int min, int max, int factor).

  • min, the minimal delay to poll the server for any pending server-push threads.
  • max, the maximum delay to poll the server for any pending server-push threads.
  • factor, The real delay is the processing time multiplies the delay factor.


Download

Summary

In this small talk, we have demonstartes you the power of server push, and it is simple to use. Besisde, server push could be applied in many applications. If you are have any comment or idea, feel free to give us feedback. And, we are looking foward for your contribution to implment other kinds of server push, for exmaple, comet. Love ZK, and have fun!




Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.